package cc.shacocloud.mirage.utils.converter.support;

import cc.shacocloud.mirage.utils.BooleanUtil;
import cc.shacocloud.mirage.utils.ByteUtil;
import cc.shacocloud.mirage.utils.ClassUtil;
import cc.shacocloud.mirage.utils.NumberUtil;
import cc.shacocloud.mirage.utils.charSequence.StrUtil;
import cc.shacocloud.mirage.utils.converter.ConversionSupport;
import cc.shacocloud.mirage.utils.converter.TypeDescriptor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Calendar;
import java.util.Date;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.DoubleAdder;
import java.util.concurrent.atomic.LongAdder;

/**
 * 转换为 {@link Number} 类型的转换器
 *
 * @author 思追(shaco)
 */
public class ToNumberConversion extends AbstractConversion implements ConversionSupport {
    
    @Override
    public boolean support(@NotNull TypeDescriptor sourceType, @NotNull TypeDescriptor targetType) {
        Class<?> objectType = targetType.getObjectType();
        return ClassUtil.isAssignable(Number.class, objectType);
    }
    
    @SuppressWarnings("unchecked")
    @Override
    public Object convert(@NotNull Object source,
                          @NotNull TypeDescriptor sourceType,
                          @NotNull TypeDescriptor targetType) {
        Class<?> objectType = targetType.getObjectType();
        Number number = convert(source, (Class<? extends Number>) objectType);
        
        if (Objects.isNull(number)) {
            number = internalConvert(source, sourceType, targetType);
        }
        
        if (Objects.isNull(number)) {
            throw new UnsupportedOperationException(String.format("不支持数值类型: %s", targetType.getObjectType()));
        }
        
        return number;
    }
    
    /**
     * 转换对象为数字，支持的对象包括：
     *
     * @param source     源对象值
     * @param targetType 目标的数字类型
     * @return 转换后的数字
     */
    @Nullable
    protected Number convert(@NotNull Object source,
                             Class<? extends Number> targetType) {
        // 枚举转换为数字默认为其顺序
        if (source instanceof Enum) {
            return convert(((Enum<?>) source).ordinal(), targetType);
        }
        // 字节数组
        else if (source instanceof byte[]) {
            return ByteUtil.bytesToNumber((byte[]) source, targetType, ByteUtil.DEFAULT_ORDER);
        }
        // Byte
        else if (Byte.class.equals(targetType)) {
            if (source instanceof Number) {
                return ((Number) source).byteValue();
            } else if (source instanceof Boolean) {
                return BooleanUtil.toByteObj((Boolean) source);
            }
            final String valueStr = convertToStr(source);
            try {
                return StrUtil.isBlank(valueStr) ? null : Byte.valueOf(valueStr);
            } catch (NumberFormatException e) {
                return NumberUtil.parseNumber(valueStr).byteValue();
            }
        }
        // Short
        else if (Short.class.equals(targetType)) {
            if (source instanceof Number) {
                return ((Number) source).shortValue();
            } else if (source instanceof Boolean) {
                return BooleanUtil.toShortObj((Boolean) source);
            }
            final String valueStr = convertToStr((source));
            try {
                return StrUtil.isBlank(valueStr) ? null : Short.valueOf(valueStr);
            } catch (NumberFormatException e) {
                return NumberUtil.parseNumber(valueStr).shortValue();
            }
        }
        // Integer
        else if (Integer.class.equals(targetType)) {
            if (source instanceof Number) {
                return ((Number) source).intValue();
            } else if (source instanceof Boolean) {
                return BooleanUtil.toInteger((Boolean) source);
            } else if (source instanceof Date) {
                return (int) ((Date) source).getTime();
            } else if (source instanceof Calendar) {
                return (int) ((Calendar) source).getTimeInMillis();
            }
            final String valueStr = convertToStr((source));
            return StrUtil.isBlank(valueStr) ? null : NumberUtil.parseInt(valueStr);
        }
        // AtomicInteger
        else if (AtomicInteger.class.equals(targetType)) {
            final Number number = convert(source, Integer.class);
            if (null != number) {
                return new AtomicInteger(number.intValue());
            }
        }
        // Long
        else if (Long.class.equals(targetType)) {
            if (source instanceof Number) {
                return ((Number) source).longValue();
            } else if (source instanceof Boolean) {
                return BooleanUtil.toLongObj((Boolean) source);
            } else if (source instanceof Date) {
                return ((Date) source).getTime();
            } else if (source instanceof Calendar) {
                return ((Calendar) source).getTimeInMillis();
            }
            final String valueStr = convertToStr((source));
            return StrUtil.isBlank(valueStr) ? null : NumberUtil.parseLong(valueStr);
        }
        // AtomicLong
        else if (AtomicLong.class.equals(targetType)) {
            final Number number = convert(source, Long.class);
            if (null != number) {
                return new AtomicLong(number.longValue());
            }
        }
        // LongAdder
        else if (LongAdder.class.equals(targetType)) {
            //jdk8 新增
            final Number number = convert(source, Long.class);
            if (null != number) {
                final LongAdder longValue = new LongAdder();
                longValue.add(number.longValue());
                return longValue;
            }
        }
        // Float
        else if (Float.class.equals(targetType)) {
            if (source instanceof Number) {
                return ((Number) source).floatValue();
            } else if (source instanceof Boolean) {
                return BooleanUtil.toFloatObj((Boolean) source);
            }
            final String valueStr = convertToStr((source));
            return StrUtil.isBlank(valueStr) ? null : NumberUtil.parseFloat(valueStr);
        }
        // Double
        else if (Double.class.equals(targetType)) {
            if (source instanceof Number) {
                return NumberUtil.toDouble((Number) source);
            } else if (source instanceof Boolean) {
                return BooleanUtil.toDoubleObj((Boolean) source);
            }
            final String valueStr = convertToStr((source));
            return StrUtil.isBlank(valueStr) ? null : NumberUtil.parseDouble(valueStr);
        }
        // DoubleAdder
        else if (DoubleAdder.class.equals(targetType)) {
            //jdk8 新增
            final Number number = convert(source, Double.class);
            if (null != number) {
                final DoubleAdder doubleAdder = new DoubleAdder();
                doubleAdder.add(number.doubleValue());
                return doubleAdder;
            }
        }
        // BigDecimal
        else if (BigDecimal.class.equals(targetType)) {
            return toBigDecimal(source);
        }
        // BigInteger
        else if (BigInteger.class == targetType) {
            return toBigInteger(source);
        }
        // Number
        else if (Number.class.equals(targetType)) {
            if (source instanceof Number) {
                return (Number) source;
            } else if (source instanceof Boolean) {
                return BooleanUtil.toInteger((Boolean) source);
            }
            final String valueStr = convertToStr((source));
            return StrUtil.isBlank(valueStr) ? null : NumberUtil.parseNumber(valueStr);
        }
        
        return null;
    }
    
    /**
     * 内部的转换，用于子类继承拓展
     */
    protected Number internalConvert(@NotNull Object source,
                                     @NotNull TypeDescriptor sourceType,
                                     @NotNull TypeDescriptor targetType) {
        throw new UnsupportedOperationException(String.format("不支持数值类型: %s", targetType.getObjectType()));
    }
    
    /**
     * 数值转换为字符串考虑结尾类型标识
     */
    @Override
    protected String convertToStr(@NotNull Object value) {
        String result = super.convertToStr(value);
        result = StrUtil.trim(result);
        if (null != result && result.length() > 1) {
            final char c = Character.toUpperCase(result.charAt(result.length() - 1));
            if (c == 'D' || c == 'L' || c == 'F') {
                // 类型标识形式（例如123.6D）
                return StrUtil.subPre(result, -1);
            }
        }
        return result;
    }
    
    /**
     * 转换为BigDecimal<br>
     * 如果给定的值为空，或者转换失败，返回默认值<br>
     * 转换失败不会报错
     *
     * @param value 被转换的值
     * @return 结果
     */
    protected BigDecimal toBigDecimal(Object value) {
        if (value instanceof Number) {
            return NumberUtil.toBigDecimal((Number) value);
        } else if (value instanceof Boolean) {
            return ((boolean) value) ? BigDecimal.ONE : BigDecimal.ZERO;
        }
        
        //对于Double类型，先要转换为String，避免精度问题
        return NumberUtil.toBigDecimal(convertToStr(value));
    }
    
    /**
     * 转换为BigInteger<br>
     * 如果给定的值为空，或者转换失败，返回默认值<br>
     * 转换失败不会报错
     *
     * @param value 被转换的值
     * @return 结果
     */
    protected BigInteger toBigInteger(Object value) {
        if (value instanceof Long) {
            return BigInteger.valueOf((Long) value);
        } else if (value instanceof Boolean) {
            return (boolean) value ? BigInteger.ONE : BigInteger.ZERO;
        }
        
        return NumberUtil.toBigInteger(convertToStr(value));
    }
    
}
